// * splice 方法：可以添加、删除和插入元素。会改变原数组。
// 语法：arr.splice(索引[，删除个数，要插入的元素])

// * 从索引 1 开始删除，删除一个元素。
let arr = ['I', 'study', 'JavaScript']
arr.splice(1, 1)
console.log(arr) // [ 'I', 'JavaScript' ]

// * 从索引 0 开始删除，删除 3 个元素，再插入两个元素。
let arr2 = [1, 2, 3, 4, 5, 6]
arr2.splice(0, 3, 'one', 'two')
console.log(arr2) // [ 'one', 'two', 4, 5, 6 ]

// * 获取被删除的元素
let arr3 = [1, 2, 3]
let removed = arr3.splice(0, 2)
console.log(removed) // [ 1, 2 ]
console.log(arr3) // [ 3 ]

// * 在指定位置插入元素，不删除元素
let arr4 = [1, 2, 3]
// 2 表示在索引 2 的位置前面插入
arr4.splice(2, 0, '插入1', '插入2')
console.log(arr4) // [ 1, 2, '插入1', '插入2', 3 ]

// * 允许负向索引
let arr5 = [1, 2, 5]
// -1 表示最后一个元素，然后在最后一个元素前面插入
arr5.splice(-1, 0, '插入1', '插入2')
console.log(arr5) // [ 1, 2, '插入1', '插入2', 5 ]

// * 遍历：forEach
let arrs = [1, 2, 3]
arrs.forEach((item, index, array) => {
	console.log(item, index, array) // 1 0 [ 1, 2, 3 ], ...
})

// ******************************************************************************************

// * 以下方法均不会改变原数组，而是返回一个新数组。

// * arr.slice([strat], [end]) 方法：从索引 start 到 end（不包括 end），截取元素，返回一个新的数组。
let arr6 = ['t', 'e', 's', 't']
console.log(arr6.slice(1, 3)) // [ 'e', 's' ]
// 从倒数第二位开始截取
console.log(arr6.slice(-2)) // [ 's', 't' ]
// 通常用于创建一个副本
let copyArr6 = arr6.slice()
console.log(copyArr6) // [ 't', 'e', 's', 't' ]

// * arr.concat(arg1, arg2...) 方法：创建一个新数组，包含原数组和来自于其他数组和其他项的值。
let arr7 = [1, 2, 3]
console.log(arr7.concat([4, 5])) // [ 1, 2, 3, 4, 5 ]
console.log(arr7.concat([4, 5], 6, 7, 8)) // [ 1, 2, 3, 4, 5, 6, 7, 8 ]
console.log(arr7.concat([4, 5], { name: 'Jake' })) // [ 1, 2, 3, 4, 5, { name: 'Jake' } ]

// ******************************************************************************************

// * 在数组中搜索

// * arr.indexOf(item, from)/arr.lastIndexOf(item, from) —— 从 from 位置开始搜索 item，如果找到则返回索引，否则返回 -1。（使用 === 严格比较）
// * 【推荐使用】arr.includes(item, form) —— 从索引 from 开始搜索 item，如果找到则返回 true，否则返回 false。（使用 === 严格比较）

let arrFliter = [1, 0, false, 0, NaN]

console.log(arrFliter.indexOf(0)) // 1
console.log(arrFliter.indexOf(0, 2)) // 3
console.log(arrFliter.indexOf(5)) // -1

console.log(arrFliter.includes(0)) // true
console.log(arrFliter.includes(0, 2)) // true
console.log(arrFliter.includes(5)) // false

// includes 可以正确处理 NaN，但 indexOf 不可以。
console.log(arrFliter.indexOf(NaN)) // -1
console.log(arrFliter.includes(NaN)) // true

// * arr.find 和 arr.findIndex/arr.findLastIndex 查找对象数组中，符合特定条件的第一个对象。
let objArr = [{ name: 'Jake' }, { name: 'John' }, { name: 'Jane' }, { name: 'John' }]
let result = objArr.find((item, index, array) => item.name === 'John') // 如果条件为 true，则返回 item，否则返回 undefined。
console.log(result) // { name: 'John' }

// * arr.findIndex 相同的查找方式（符合条件的第一个对象），但返回的是索引，没找到返回 -1。
let index = objArr.findIndex((item) => item.name === 'John')
console.log(index) // 1

// * filter 匹配所有符合条件的元素，返回一个新数组，没找到返回空数组。
let result2 = objArr.filter((item) => item.name === 'John')
let result3 = objArr.filter((item) => item.name === 'lili')
console.log(result2) // [ { name: 'John' }, { name: 'John' } ]
console.log(result3) // []

// ******************************************************************************************

// * 转换数组

// * arr.map 对数组元素进行转换并返回一个新数组。
let arr8 = [1, 2, 3]
let arr9 = [{ name: 'Jake' }, { name: 'John' }]
console.log(arr8.map((item) => item * 2)) // [ 2, 4, 6 ]
console.log(arr9.map((item) => ({ name: `我的名字是：${item.name}` }))) // [ { name: '我的名字是：Jake' }, { name: '我的名字是：John' } ]

// ******************************************************************************************

// * arr.sort(fn) 数组排序，会修改数组本身。默认情况下被按字符串进行排序。
let arr10 = [1, 2, 15]
arr10.sort()
console.log(arr10) // [ 1, 15, 2 ] 元素默认情况下转换成字符串比较进行排序，所以 15 会排在 2 的前面。

// * 给 sort() 提供一个函数作为参数，用来自定义排序规则。
function compare(a, b) {
	if (a > b) return 1 // 如果比较函数返回正数，表示第一个元素应该排在第二个元素之后。
	if (a < b) return -1 // 如果比较函数返回负数，表示第一个元素应该排在第二个元素之前。
	return 0 // 如果比较函数返回零，表示两个元素相等，顺序保持不变。
}

// * 比较函数可以返回任何数字，实际上，比较函数只需要返回一个正数表示大于，一个负数表示小于。所以可以书写下面的简写形式。

let arr11 = [1, 2, 15]
// 正序，a - b 为正数，a 在 b 的前面，为负数则在 b 的后面。
arr11.sort((a, b) => a - b)
console.log(arr11) // [ 1, 2, 15 ]
// 倒序
arr11.sort((a, b) => b - a)
console.log(arr11) // [ 15, 2, 1 ]

// 对象排序例子：
let users = [
	{ id: 4, name: 'John' },
	{ id: 2, name: 'John' },
	{ id: 1, name: 'Jake' },
	{ id: 3, name: 'Jane' },
]

users.sort((a, b) => a.id - b.id)
console.log(users) // [ { id: 1, name: 'Jake' }, { id: 2, name: 'John' }, { id: 3, name: 'Jane' }, { id: 4, name: 'John' } ]

// ******************************************************************************************

// * reverse 反转数组。会修改数组本身。
let arr12 = [1, 2, 3, 4, 5, 6, { name: 'Jake' }, { name: 'John' }]
arr12.reverse()
console.log(arr12) // [ { name: 'John' }, { name: 'Jake' }, 6, 5, 4, 3, 2, 1 ]

// ******************************************************************************************

// split(按什么分隔符进行分隔[, 限制数组的长度])

// 拆分字符串
let names = 'Bilbo, Gandalf, Nazgul'
let nameArr = names.split(', ')
console.log(nameArr) // [ 'Bilbo', 'Gandalf', 'Nazgul' ]

// 拆分字母
let str = 'test'
console.log(str.split('')) // [ 't', 'e', 's', 't' ]
console.log(str.split(' ')) // [ 'test' ]

// arr.join(glue) 将数组转换为字符串（）
let strs1 = ['a', 'b', 'c'].join()
let strs2 = ['a', 'b', 'c'].join(' - ')
console.log(strs1) // a,b,c 默认情况
console.log(strs2) // a - b - c

// ******************************************************************************************

// * reduce/reduceRight 使用此方法时，建议始终指定初始值。循环执行完毕后，会将第一个参数（累加器）的执行结果进行返回。

// 数组求和
let numsArr = [1, 2, 3, 4, 5, 6]
let value1 = numsArr.reduce((sum, item, index, array) => sum + item, 0)
console.log(value1) // 21

// 对象数组去重
let objsArr = [{ name: 'Jake' }, { name: 'Jake' }, { name: 'John' }]
let res = objsArr.reduce((newArr, item) => {
	if (newArr.findIndex((i) => i.name == item.name) === -1) {
		newArr.push(item)
	}
	return newArr
}, [])

console.log(res) // [ { name: 'Jake' }, { name: 'John' } ]

// ******************************************************************************************

// Array.isArray 判断是否是数组

console.log(Array.isArray({})) // false
console.log(Array.isArray([])) // true

// ******************************************************************************************

// find, filter, map 它们的第二个参数(func[, thisArg]) 是用来指定 this 的值。

// 例子：
let army = {
	minAge: 18,
	maxAge: 27,
	canJoin(user) {
		return user.age >= this.minAge && user.age < this.maxAge
	},
}

let userList = [
	{
		age: 16,
	},
	{
		age: 20,
	},
	{
		age: 23,
	},
	{
		age: 30,
	},
]

let soldiers = userList.filter(army.canJoin, army)

console.log(soldiers) // [ { age: 20 }, { age: 23 } ]
